#pragma once
#include "stdafx.h"

namespace hhfox {

class Buffer {
public:
    Buffer(char* b, int nn);
    ~Buffer();

public:
    Buffer* copy();
    char* data();
    char* head();
    void seek(size_t pos);
    int size();
    void set_size(int v);

    int pos();
    int left();

    bool empty();
    bool require(int required_size);

public:
    void skip(int size);

public:
    int8_t read_1bytes();
    int16_t read_2bytes();
    int16_t read_le2bytes();
    int32_t read_3bytes();
    int32_t read_le3bytes();
    template <typename T>
    T read_4bytes() {
        assert(require(4));
        T value = 0x00;
        char* pp = (char*)&value;
        pp[3] = *p++;
        pp[2] = *p++;
        pp[1] = *p++;
        pp[0] = *p++;
        return value;
    }

    template <typename T>
    T read_le4bytes() {
        assert(require(4));
        T value = 0x00;
        char* pp = (char*)&value;
        pp[0] = *p++;
        pp[1] = *p++;
        pp[2] = *p++;
        pp[3] = *p++;
        return value;
    }

    template <typename T>
    T read_8bytes() {
        assert(require(8));
        T value = 0x00;
        char* pp = (char*)&value;
        pp[7] = *p++;
        pp[6] = *p++;
        pp[5] = *p++;
        pp[4] = *p++;
        pp[3] = *p++;
        pp[2] = *p++;
        pp[1] = *p++;
        pp[0] = *p++;
        return value;
    }

    template <typename T>
    T read_le8bytes() {
        assert(require(8));
        T value = 0x00;
        char* pp = (char*)&value;
        pp[0] = *p++;
        pp[1] = *p++;
        pp[2] = *p++;
        pp[3] = *p++;
        pp[4] = *p++;
        pp[5] = *p++;
        pp[6] = *p++;
        pp[7] = *p++;
        return value;
    }

    std::string read_string(int len);
    void read_byte(char* data, int size);

public:
    void write_1bytes(int8_t value);
    void write_2bytes(int16_t value);
    void write_le2bytes(int16_t value);
    void write_3bytes(int32_t value);
    void write_le3bytes(int32_t value);

    template <typename T>
    void write_4bytes(T value) {
        assert(require(4));
        char* pp = (char*)&value;
        *p++ = pp[3];
        *p++ = pp[2];
        *p++ = pp[1];
        *p++ = pp[0];
    }

    template <typename T>
    void write_le4bytes(T value) {
        assert(require(4));
        char* pp = (char*)&value;
        *p++ = pp[0];
        *p++ = pp[1];
        *p++ = pp[2];
        *p++ = pp[3];
    }
    void write_8bytes(int64_t value);
    void write_le8bytes(int64_t value);

    template <typename T>
    void write_8bytes(T value) {
        assert(require(8));
        char* pp = (char*)&value;
        *p++ = pp[7];
        *p++ = pp[6];
        *p++ = pp[5];
        *p++ = pp[4];
        *p++ = pp[3];
        *p++ = pp[2];
        *p++ = pp[1];
        *p++ = pp[0];
    }

    template <typename T>
    void write_le8bytes(T value) {
        assert(require(8));
        char* pp = (char*)&value;
        *p++ = pp[0];
        *p++ = pp[1];
        *p++ = pp[2];
        *p++ = pp[3];
        *p++ = pp[4];
        *p++ = pp[5];
        *p++ = pp[6];
        *p++ = pp[7];
    }

    void write_string(std::string value);
    void write_bytes(char* data, int size);

private:
    char* p;
    char* bytes;
    int nb_bytes;
};

class VBuffer {
public:
    VBuffer(std::vector<unsigned char>& b) : buffer_(b) {}
    ~VBuffer();
    void seek(size_t pos);
    void write_1bytes(int8_t value);
    void write_2bytes(int16_t value);
    void write_le2bytes(int16_t value);
    void write_3bytes(int32_t value);
    void write_le3bytes(int32_t value);
    template <typename T>
    void write_4bytes(T value) {
        char* pp = (char*)&value;
        buffer_.push_back(pp[3]);
        buffer_.push_back(pp[2]);
        buffer_.push_back(pp[1]);
        buffer_.push_back(pp[0]);
        current_pos_ += 4;
    }

    template <typename T>
    void write_le4bytes(T value) {
        char* pp = (char*)&value;
        buffer_.push_back(pp[0]);
        buffer_.push_back(pp[1]);
        buffer_.push_back(pp[2]);
        buffer_.push_back(pp[3]);
        current_pos_ += 4;
    }

    template <typename T>
    void write_8bytes(T value) {
        char* pp = (char*)&value;
        buffer_.push_back(pp[7]);
        buffer_.push_back(pp[6]);
        buffer_.push_back(pp[5]);
        buffer_.push_back(pp[4]);
        buffer_.push_back(pp[3]);
        buffer_.push_back(pp[2]);
        buffer_.push_back(pp[1]);
        buffer_.push_back(pp[0]);
        current_pos_ += 8;
    }
    template <typename T>
    void write_le8bytes(T value) {
        char* pp = (char*)&value;
        buffer_.push_back(pp[0]);
        buffer_.push_back(pp[1]);
        buffer_.push_back(pp[2]);
        buffer_.push_back(pp[3]);
        buffer_.push_back(pp[4]);
        buffer_.push_back(pp[5]);
        buffer_.push_back(pp[6]);
        buffer_.push_back(pp[7]);
        current_pos_ += 8;
    }

    void write_bytes(unsigned char* data, int size);

private:
    std::vector<unsigned char>& buffer_;
    size_t current_pos_ = 0;
};

} // namespace hhfox